package com.vr.xuecheng.orders.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConstants;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vr.xuecheng.base.exception.XueChengPlusException;
import com.vr.xuecheng.common.util.SecurityUtils;
import com.vr.xuecheng.messagesdk.model.entity.MqMessage;
import com.vr.xuecheng.messagesdk.service.MqMessageService;
import com.vr.xuecheng.orders.constant.RabbitMqConstants;
import com.vr.xuecheng.orders.dto.AddOrderDTO;
import com.vr.xuecheng.orders.entity.Orders;
import com.vr.xuecheng.orders.entity.PayRecord;
import com.vr.xuecheng.orders.mapper.OrdersMapper;
import com.vr.xuecheng.orders.service.IOrdersGoodsService;
import com.vr.xuecheng.orders.service.IOrdersService;
import com.vr.xuecheng.orders.service.IPayRecordService;
import com.vr.xuecheng.orders.util.SnowFlakeUtils;
import com.vr.xuecheng.orders.vo.PayRecordVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService {
    @Value("${pay.qrcode-url}")
    private String qrcodeUrlTemplate;
    @Value("${alipay.notify-url}")
    private String notifyUrl;
    @Value("${alipay.public-key}")
    private String publicKey;
    @Autowired
    @Lazy
    private IOrdersService ordersService;
    private final IPayRecordService payRecordService;
    private final IOrdersGoodsService ordersGoodsService;
    private final AlipayClient alipayClient;
    private final MqMessageService mqMessageService;
    private final RabbitTemplate rabbitTemplate;

    @Override
    @Transactional
    public PayRecordVO generatePayCode(AddOrderDTO addOrderDto) {
        SecurityUtils.User currentUser = SecurityUtils.getUser();
        if (currentUser == null) {
            XueChengPlusException.cast("请登录后继续选课");
        }
        String userId = currentUser.getId();
        // 添加商品订单
        Orders ordersOfAdd = ordersService.saveOrders(userId, addOrderDto);
        // 创建支付记录
        PayRecord payRecords = payRecordService.createPayRecords(ordersOfAdd);
        // 生成支付二维码
        String qrcodeUrl = null;
        QrConfig qrConfig = null;
        try {
            qrcodeUrl = String.format(qrcodeUrlTemplate, payRecords.getPayNo());
            qrConfig = QrConfig.create()
                    .setWidth(200)
                    .setHeight(200);
        } catch (Exception e) {
            XueChengPlusException.cast("生成二维码出错");
        }
        String url = QrCodeUtil.generateAsBase64(qrcodeUrl, qrConfig, "png");
        PayRecordVO payRecordVO = new PayRecordVO();
        BeanUtil.copyProperties(payRecords, payRecordVO);
        payRecordVO.setQrcode(url);
        return payRecordVO;
    }

    @Override
    public Orders saveOrders(String userId, AddOrderDTO addOrderDto) {
        // 检查当前用户是是否存在该未支付的订单防止重复下单
        Orders queryResult = ordersService.getOrdersByOutBusinessId(addOrderDto.getOutBusinessId());
        if (queryResult != null) {
            return queryResult;
        }
        Orders waitOrder = Orders.builder()
                // 雪花算法生成订单id,保证不重复
                .id(SnowFlakeUtils.nextId())
                .totalPrice(addOrderDto.getTotalPrice())
                // 订单交易类型，未支付
                .status("600001")
                .userId(userId)
                .orderType(addOrderDto.getOrderType())
                .orderName(addOrderDto.getOrderName())
                .orderDetail(addOrderDto.getOrderDetail())
                .orderDescrip(addOrderDto.getOrderDescrip())
                // 选课记录
                .outBusinessId(addOrderDto.getOutBusinessId())
                .build();
        ordersService.save(waitOrder);
        // 保存订单明细
        ordersGoodsService.saveOrdersGoods(waitOrder.getId(), waitOrder.getOrderDetail());
        return waitOrder;
    }

    @Override
    public Orders getOrdersByOutBusinessId(String outBusinessId) {
        return ordersService.lambdaQuery()
                .eq(Orders::getOutBusinessId, outBusinessId)
                .one();
    }

    @Override
    public void pay(String payNo, HttpServletResponse httpResponse) {
        // 判断支付记录是否存在
        PayRecord payRecord = payRecordService.lambdaQuery()
                .eq(PayRecord::getPayNo, payNo)
                .one();
        if (payRecord == null) {
            XueChengPlusException.cast("请重新点击支付获取二维码");
        }
        if ("601002".equals(payRecord.getStatus())) {
            XueChengPlusException.cast("订单已支付,请不要重复支付");
        }
        // 支付
        AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
        request.setNotifyUrl(notifyUrl);
        AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
        // 交易号
        model.setOutTradeNo(payRecord.getPayNo().toString());
        // 订单总金额
        model.setTotalAmount(payRecord.getTotalPrice().toString());
        // 订单名称
        model.setSubject(payRecord.getOrderName());
        model.setProductCode("QUICK_WAP_PAY");
        request.setBizModel(model);
        AlipayTradeWapPayResponse response;
        String form = "";
        try {
            response = alipayClient.pageExecute(request, "POST");
            form = response.getBody();
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
        if (response.isSuccess()) {
            System.out.println("调用成功");
        }
        httpResponse.setContentType("text/html;charset=" + AlipayConstants.CHARSET_UTF8);
        // 直接将完整的表单html输出到页面
        try {
            httpResponse.getWriter().write(form);
            httpResponse.getWriter().flush();
            httpResponse.getWriter().close();
        } catch (IOException e) {
            log.error("写入页面失败");
            throw new RuntimeException(e);
        }
    }

    @Override
    @Transactional
    public void receiveNotify(HttpServletRequest request, HttpServletResponse out) {
        // 获取参数
        Map<String, String> params = this.covertRequestToMap(request);
        // 校验签名
        boolean check = false;
        try {
            check = AlipaySignature.rsaCheckV1(
                    params,
                    publicKey,
                    AlipayConstants.CHARSET_UTF8,
                    AlipayConstants.SIGN_TYPE_RSA2
            );
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
        if (!check) {
            XueChengPlusException.cast("签名验证失败");
        }
        // 校验交易是否成功
        String tradeStatus = params.get("trade_status");
        if (!"TRADE_SUCCESS".equals(tradeStatus)) {
            XueChengPlusException.cast("交易失败");
        }
        // 更新支付记录和订单记录
        Orders orders = ordersService.updateOrderAndPayRecord(params);
        // 通过mq 通知更新学习中心服务
        ordersService.payNotifyLearning(orders);

    }

    @Override
    public Orders updateOrderAndPayRecord(Map<String,String> params) {
        // 根据交易号拿到当前的支付记录
        String payNo = params.get("out_trade_no");
        PayRecord payRecord = payRecordService.lambdaQuery()
                .eq(PayRecord::getPayNo, payNo)
                .one();
        if (payRecord == null) {
            XueChengPlusException.cast("当前交易不存在，请联系管理员");
        }
        Orders orders = ordersService.lambdaQuery()
                .eq(Orders::getId, payRecord.getOrderId())
                .one();
        if (orders == null) {
            XueChengPlusException.cast("当前订单不存在，请联系管理员");
        }
        payRecord.setStatus("601002")
                .setOutPayNo(params.get("trade_no"))
                .setPaySuccessTime(LocalDateTime.now());
        orders.setStatus("600002");
        // 更新支付记录
        payRecordService.updateById(payRecord);
        // 更新订单
        ordersService.updateById(orders);
        return orders;
    }

    @Override
    public void payNotifyLearning(Orders orders) {
        // 创建消息
        MqMessage mqMessage = mqMessageService.addMessage("payresult_notify", orders.getOutBusinessId(), orders.getOrderType(), null);
        // 通知学习中心服务通过mq
        CorrelationData correlationData = new CorrelationData();
        correlationData.setId(mqMessage.getId().toString());
        correlationData.getFuture().completable().thenAccept(result -> {
            if (result.isAck()) {
                log.debug("通知支付结果消息发送成功,ID:{}", correlationData.getId());
                // 删除消息记录表的消息
                mqMessageService.completed(mqMessage.getId());
            } else {
                log.error("通知支付结果消息发送失败,ID:{},原因:{}", correlationData.getId(), result.getReason());
            }
        });
        rabbitTemplate.convertAndSend(RabbitMqConstants.PAY_NOTIFY_EXCHANGE, RabbitMqConstants.PAY_NOTIFY_ROUTING_KEY,mqMessage,correlationData);


    }

    private Map<String, String> covertRequestToMap(HttpServletRequest request) {
        HashMap<String, String> params = new HashMap<>();
        Map<String, String[]> parameterMap = request.getParameterMap();
        for (String key : parameterMap.keySet()) {
            String[] values = parameterMap.get(key);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(key, valueStr);
        }
        return params;
    }
}
